/*
*
*   openomy-csharp 0.6: C# API for Openomy file system.
*
*   Copyright (C) 2006 Carlo Gherarducci (carlo.gherarducci(at)gmail.com)
*
* NOTES:
*	0.5: first release
*
* 	Using regular expressions instead of XML parsers for server responses. Have to do some tests to see
*	which methods is faster.
*
* TODO:
* 	Error and exception check.
*	Proxy testing.
*	
* THANKS;
*	To Ian Sefferman for his work, his patience and his help.
*	Maurice Codik and Manpreet Singh for their bindings (respectively for ruby and python), which
*	inspired and helped me with the code.
*
*
*   Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
*   associated documentation files (the "Software"), to deal in the Software without restriction,
*   including without limitation the rights to use, copy, modify, merge, publish, distribute,
*   sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
*   furnished to do so, subject to the following conditions:
*
*   The above copyright notice and this permission notice shall be included in all copies or substantial
*   portions of the Software.
*
*   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
*   LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
*   IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
*   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
*   SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Security.Cryptography;
using System.Net;
using System.Collections.Specialized;
using System.Collections;
using System.Text.RegularExpressions;
using System.Threading;
using System.Diagnostics;

namespace PocketLibrary.Storage
{
    /// <summary>
    /// Communication class for Openomy, a tag-based online file system (http://www.openomy.com)
    /// All API calls are HTTP based method calls. Server responses are in XML format.
    /// This version uses exclusively the AuthorizeUser method to log in,
    /// so GetConfirmed/UnconfirmedToken methods are not present, such as
    /// unconfirmedToken variable.
    /// </summary>
    public class Openomy
    {
        public delegate void TransferProgressHandler(FileTransferArgs args);
        public event TransferProgressHandler OnTransfer;
        public event TransferProgressHandler OnTransferComplete;

        public event EventHandler OnTransfedFailed;

        


        void TransferChunk(long filesize, long transferred)
        {
            if (OnTransfer != null)
            {
                OnTransfer(new FileTransferArgs(filesize, transferred));
            }
        }

        void TransferComplete(long filesize, long transferred)
        {
            if (OnTransferComplete != null)
            {
                OnTransferComplete(new FileTransferArgs(filesize, transferred));
            }

        }

        void TransferFailed()
        {
            if (OnTransfedFailed != null)
            {
                OnTransfedFailed(this, new EventArgs());
            }
        }

        private string applicationKey;
        private string privateKey;
        private string username;
        private string password;

        private string openomyRootURL;

        private string confirmedToken;

        public Openomy(string username, string password, string applicationKey, string privateKey)
        {
            openomyRootURL = "http://www.openomy.com/api/rest/";
            confirmedToken = "";

            this.username = username;
            this.password = password;
            this.applicationKey = applicationKey;
            this.privateKey = privateKey;
        }


        #region File methods

        /// <summary>
        /// Gets a file from the file system.
        /// </summary>
        /// <param name="fileID">The ID of the file to be downloaded.</param>
        /// <param name="timeout">Number of minutes of how long to allow the download
        /// <param name="getData">true if you want to download the binary data of the file.</param>
        /// link to be active. Default is 1, max is 20.</param>
        /// <returns>An OpenomyFile object.</returns>
        public OpenomyFile GetFile(long fileID, long timeout, bool getData)
        {
            ListDictionary parameters = new ListDictionary();

            parameters["method"] = "Files.GetFile";
            parameters["fileID"] = Convert.ToString(fileID);
            parameters["timeout"] = Convert.ToString(timeout);

            FillParams(ref parameters);

            string res = Stream2String(SendGETRequest(GenerateQueryString(parameters)));
            res = Regex.Replace(res, @"\s*\r?\n\s*", "");
            OpenomyFile of = null;

            if (!CheckError(res))
            {
                Match m = Regex.Match(res, "<file id=\"(.*?)\"><filename>(.*?)</filename>.*?<baseurl>(.*?)</baseurl><filetoken>(.*?)</filetoken>");
                of = new OpenomyFile(m.Groups[1].Value, m.Groups[2].Value);

                m = Regex.Match(res, "<created>(.*?)</created>.*?<size units=\".*?\">(.*?)</size>");
                of.creationDate = m.Groups[1].Value;
                of.size = Convert.ToInt32(m.Groups[2].Value);

                if (getData)
                {
                    m = Regex.Match(res, "<baseurl>(.*?)</baseurl><filetoken>(.*?)</filetoken>");
                    of.fileData = FileDownload(m.Groups[1].Value, m.Groups[2].Value, of.size);
                }

            }
            return (of);

        }


        /// <summary>
        /// Gets the stream from the specified file token
        /// </summary>
        /// <param name="baseAddress">The address to download from.</param>
        /// <param name="fileToken">The token of the file.</param>
        /// <param name="size">Size in bytes of the file.</param>
        /// <returns>The data array of the file.</returns>
        private byte[] FileDownload(string baseAddress, string fileToken, long size)
        {
            byte[] buffer = null;

            ListDictionary parameters = new ListDictionary();
            parameters["fileToken"] = fileToken;
            FillParams(ref parameters);

            try
            {
                string uri = baseAddress + GenerateQueryString(parameters);
                Console.WriteLine("\nFile URI: " + uri);

                HttpWebRequest req = (HttpWebRequest)WebRequest.Create(uri);
                req.Method = "GET";
                req.UserAgent = "TaskuBlogi 1.0";

                HttpWebResponse resp = (HttpWebResponse)req.GetResponse();

                // Retrieve response stream
                Stream respStream = resp.GetResponseStream();

                // Create local file
                //FileStream wrtr = new FileStream(localFile, FileMode.Create);
                MemoryStream wrtr = new MemoryStream();

                // Allocate byte buffer to hold stream contents
                byte[] inData = new byte[4096];

                long transfer = 0;
                // loop through response stream reading each data block
                //  and writing to the local file
                int bytesRead = respStream.Read(inData, 0, inData.Length);
                while (bytesRead > 0)
                {
                    TransferChunk(size, transfer);
                    wrtr.Write(inData, 0, bytesRead);
                    bytesRead = respStream.Read(inData, 0, inData.Length);
                    transfer = bytesRead + transfer;
                }

                respStream.Close();
                TransferComplete(size, transfer);

                buffer = wrtr.GetBuffer();
                //wrtr.Read(buffer, 0, buffer.Length);
                wrtr.Close();
            }
            catch (WebException we) { Console.WriteLine("Error while downloading file:" + we); }

            return (buffer);
        }

        /// <summary>
        /// Adds a file to the file system.
        /// </summary>
        /// <param name="fileName">Name of uploaded file (only that, without path).</param>
        /// <param name="fileData">Binary data of the file.</param>
        /// <param name="tagID">The tag id to which the file will be associated.</param>
        /// <returns>The id of the new file, or -1 if unsuccess.</returns>
        public long AddFile(string fileName, byte[] fileData, long tagID)
        {
            ListDictionary parameters = new ListDictionary();
            parameters["method"] = "Files.AddFile";

            parameters["applicationKey"] = applicationKey;

            //needed by all methods except the authorization ones.
            if (!confirmedToken.Equals("")) parameters["confirmedToken"] = confirmedToken;

            /*
               my $unhashed  = "applicationKey=$applicationKey"."confirmedToken=$confirmedToken"."method=$method"."tagID=$tagID"."$privateKey";
             * */

            string signature = HelperUtils.MD5String(string.Format("applicationKey={0}confirmedToken={1}method={2}tagID={3}{4}",
                applicationKey, confirmedToken, "Files.AddFile", tagID, privateKey));

            parameters["signature"] = signature;
            parameters["tagID"] = Convert.ToString(tagID);


            Stream s = FileUpload(fileName, fileData, parameters);

            if (s == null)
                return -1;

            string res = Stream2String(s);
            Debug.WriteLine("Upload response: " + res, "OpenomyFileUpload");
            if (CheckError(res)) return (-1);

            return (Convert.ToInt32(Regex.Match(res, "<fileid>(.*?)</fileid>").Groups[1].Value));
        }


        /// <summary>
        /// Uploads a file to Openomy using POST. It's RFC 1867 compliant,
        /// see http://www.faqs.org/rfcs/rfc1867.html.
        /// </summary>
        /// <param name="fileName">Name of uploaded file (only that, without path).</param>
        /// <param name="fileData">Binary data of the file.</param>
        /// <param name="parameters">The parameters of the query string.</param>
        /// <returns>The stream to the web response.</returns>
        private Stream FileUpload(string fileName, byte[] fileData, ListDictionary parameters)
        {

            //boundary for RFC 1867 compliance.
            string boundary = "TASKUBLOGI_MIME_" + DateTime.Now.ToString("yyyyMMddhhmmss");

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(openomyRootURL);
            Stream retstream = null;

            request.Method = "POST";
            //request.Timeout = 30 * 1000;  // 10 seconds (10*1000 milliseconds)
            //request.SendChunked = true;
            request.KeepAlive = true;
            request.UserAgent = "TaskuBlogi 1.0";
            request.ContentType = "multipart/form-data; boundary=" + boundary;
            
            //WebProxy myproxy = new WebProxy("195.148.65.5", 800);
            //request.Proxy = myproxy;
            
            StringBuilder sb = new StringBuilder();

            foreach (string key in parameters.Keys)
            {
                sb.Append("--" + boundary + "\r\n");
                sb.Append("content-disposition: form-data; name=\"" + key + "\"\r\n");
                sb.Append("\r\n");
                sb.Append(parameters[key].ToString() + "\r\n");
            }

            sb.Append("--" + boundary + "\r\n");
            sb.Append("content-disposition: form-data; name=\"sendfile\"; filename=\"" + fileName + "\"\r\n"); // fileField
            sb.Append("content-type: " + ResolveMimeType(fileName) + "\r\n");
            //sb.Append("content-transfer-encoding: binary\r\n");
            sb.Append("\r\n");

            //UTF8Encoding encoding = new UTF8Encoding();
            ASCIIEncoding encoding = new ASCIIEncoding();

            byte[] postContents = encoding.GetBytes(sb.ToString());

            byte[] postFooter = encoding.GetBytes("\r\n--" + boundary + "--\r\n");

            byte[] dataBuffer = new byte[postContents.Length + fileData.Length + postFooter.Length];
            Buffer.BlockCopy(postContents, 0, dataBuffer, 0, postContents.Length);
            Buffer.BlockCopy(fileData, 0, dataBuffer, postContents.Length, fileData.Length);
            Buffer.BlockCopy(postFooter, 0, dataBuffer, postContents.Length + fileData.Length, postFooter.Length);

            request.ContentLength = dataBuffer.Length;
            Stream resStream = request.GetRequestStream();

            int uploadBit = Math.Max(dataBuffer.Length / 100, 50 * 1024);
            int uploadSoFar = 0;
            TimeSpan starttime = DateTime.Now.TimeOfDay;

            try
            {
                Debug.WriteLine("Upload start", "OpenomyFileUpload");
                Debug.WriteLine("Start time: " + starttime, "OpenomyFileUpload");

                //send the POST packet to the server
                for (int i = 0; i < dataBuffer.Length; i = i + uploadBit)
                {
                    int toUpload = Math.Min(uploadBit, dataBuffer.Length - i);
                    uploadSoFar += toUpload;

                    resStream.Write(dataBuffer, i, toUpload);

                    TransferChunk(dataBuffer.Length, i + toUpload);
                }

                resStream.Close();
                Debug.WriteLine("Upload end", "OpenomyFileUpload");

                Debug.WriteLine("Response start", "OpenomyFileUpload");
                Debug.WriteLine("HasResponse", "OpenomyFileUpload");

                HttpWebResponse res = (HttpWebResponse)request.GetResponse();
                retstream = res.GetResponseStream();
                Debug.WriteLine("Response end", "OpenomyFileUpload");
            }
            catch (ThreadAbortException ex) { System.Diagnostics.Debug.WriteLine("ThreadAbortException:" + ex.Message, "OpenomyFileUpload"); TransferFailed(); }
            catch (TimeoutException ex) { System.Diagnostics.Debug.WriteLine("TimeoutException:" + ex.Message, "OpenomyFileUpload"); TransferFailed(); }
            catch (ProtocolViolationException ex) { System.Diagnostics.Debug.WriteLine("ProtocolViolationException:" + ex.Message, "OpenomyFileUpload"); TransferFailed(); }
            catch (System.Net.Sockets.SocketException ex)
            {
                Debug.WriteLine("SocketException: " + ex.Message, "OpenomyFileUpload");
                Debug.WriteLine(ex.StackTrace, "OpenomyFileUpload");
                TransferFailed();
            }
            catch (WebException ex)
            {
                Debug.WriteLine("WebException: " + ex.Message, "OpenomyFileUpload");
                Debug.WriteLine(ex.StackTrace, "OpenomyFileUpload");
                TransferFailed();
            }
            catch (Exception ex) { System.Diagnostics.Debug.WriteLine(ex.Message, "OpenomyFileUpload"); TransferFailed(); }
            finally
            {
                TimeSpan endtime = DateTime.Now.TimeOfDay;
                TimeSpan duration = new TimeSpan(endtime.Ticks - starttime.Ticks);
                Debug.WriteLine("End time: " + endtime, "OpenomyFileUpload");
                Debug.WriteLine("Duration: " + (int)duration.TotalSeconds, "OpenomyFileUpload");
                Debug.WriteLine("TimeOut: " + (int)(request.Timeout / 1000), "OpenomyFileUpload");
                TransferComplete(dataBuffer.Length, dataBuffer.Length);
            }

            return (retstream);
        }

        /// <summary>
        /// Removes a file from the file system.
        /// </summary>
        /// <param name="fileID">The ID of the file to be deleted.</param>
        /// <returns>true if success.</returns>
        /// 
        public bool DeleteFile(long fileID)
        {
            ListDictionary parameters = new ListDictionary();

            parameters["method"] = "Files.DeleteFile";
            parameters["fileID"] = Convert.ToString(fileID);

            FillParams(ref parameters);

            return (!CheckError(Stream2String(SendGETRequest(GenerateQueryString(parameters)))));
        }


        /// <summary>
        /// Removes all files from the account. Use carefully
        /// </summary>
        /// <returns>true if success.</returns>
        private bool DeleteAllFiles()
        {

            ListDictionary parameters = new ListDictionary();

            parameters["method"] = "Files.GetAllFiles";
            FillParams(ref parameters);

            string res = Stream2String(SendGETRequest(GenerateQueryString(parameters)));

            //removes all the \n\r
            res = Regex.Replace(res, @"\s*\r?\n\s*", "");

            if (!CheckError(res))
            {
                MatchCollection mc = Regex.Matches(res, "<file id=\"(.*?)\">(.*?)</file>");

                foreach (Match m in mc)
                    DeleteFile(Convert.ToInt32(m.Groups[1].Value));
            }
            return (true);
        }

        /// <summary>
        /// Get the file list from the file system. Files are mapped to their id's.
        /// </summary>
        /// <returns>
        /// Returns an Hashtable object (filename -> fileid) containing
        /// the list of files in the file system.
        /// </returns>
        public Hashtable GetAllFiles()
        {

            ListDictionary parameters = new ListDictionary();
            Hashtable files = null;

            parameters["method"] = "Files.GetAllFiles";
            FillParams(ref parameters);

            string res = Stream2String(SendGETRequest(GenerateQueryString(parameters)));

            //removes all the \n\r
            res = Regex.Replace(res, @"\s*\r?\n\s*", "");

            if (!CheckError(res))
            {
                files = new Hashtable();
                MatchCollection mc = Regex.Matches(res, "<file id=\"(.*?)\">(.*?)</file>");

                foreach (Match m in mc)
                {
                    files.Add(m.Groups[2].Value, Convert.ToInt32(m.Groups[1].Value));
                }
            }

            return (files);
        }


        /// <summary>
        /// Replaces the file with the given id with the newly uploaded file.
        /// All tags and permissions are maintained.
        /// </summary>
        /// <param name="fileID">The ID of the file to replace.</param>
        /// <param name="fileData">The binary data of the file.</param>
        /// <returns>true if success.</returns>
        public bool ModifyFile(string fileName, long fileID, byte[] fileData)
        {
            ListDictionary parameters = new ListDictionary();
            parameters["method"] = "Files.ModifyFile";
            parameters["fileID"] = Convert.ToString(fileID);

            FillParams(ref parameters);

            Stream s = FileUpload(fileName, fileData, parameters);

            if (CheckError(Stream2String(s))) return (false);
            return (true);
        }

        #endregion


        #region Tag methods


        /// <summary>
        /// Gets information, including the name, the date created,
        /// the files within, the id, etc. about the tag in question.
        /// My version is a little different.
        /// </summary>
        /// <param name="tagID">A Hashtable containing all ids of files in the tag</param>
        public Hashtable GetTag(long tagID)
        {

            Hashtable files = null;

            ListDictionary parameters = new ListDictionary();
            parameters["method"] = "Tags.GetTag";
            parameters["tagID"] = Convert.ToString(tagID);

            FillParams(ref parameters);

            string res = Stream2String(SendGETRequest(GenerateQueryString(parameters)));

            res = Regex.Replace(res, @"\s*\r?\n\s*", "");

            if (!CheckError(res))
            {
                files = new Hashtable();
                MatchCollection mc = Regex.Matches(res, "<file id=\"(.*?)\".*?>(.*?)</file>");

                foreach (Match m in mc)
                {
                    files.Add(m.Groups[2].Value, Convert.ToInt32(m.Groups[1].Value));
                }
            }

            return (files);

        }

        /// <summary>
        /// Adds a file to a specific tag.
        /// </summary>
        /// <param name="fileID">The file ID.</param>
        /// <param name="tagID">The tag ID.</param>
        /// <returns>true if success.</returns>
        public bool AddFileToTag(long fileID, long tagID)
        {
            ListDictionary parameters = new ListDictionary();
            parameters["method"] = "Tags.AddFileToTag";
            parameters["fileID"] = Convert.ToString(fileID);
            parameters["tagID"] = Convert.ToString(tagID);

            FillParams(ref parameters);

            return (!CheckError(Stream2String(SendGETRequest(GenerateQueryString(parameters)))));
        }

        public bool DeleteFileFromTag(long fileID, long tagID)
        {
            ListDictionary parameters = new ListDictionary();
            parameters["method"] = "Tags.DeleteFileFromTag";
            parameters["fileID"] = Convert.ToString(fileID);
            parameters["tagID"] = Convert.ToString(tagID);

            FillParams(ref parameters);

            return (!CheckError(Stream2String(SendGETRequest(GenerateQueryString(parameters)))));
        }

        /// <summary>
        /// Creates a new tag associated with a user.
        /// </summary>
        /// <param name="tagName">The name of the new tag.</param>
        /// <returns>The ID of the new tag or -1 if unsuccess.</returns>
        public long CreateTag(string tagName)
        {
            ListDictionary parameters = new ListDictionary();
            parameters["method"] = "Tags.CreateTag";
            parameters["tagName"] = tagName;

            FillParams(ref parameters);

            string res = Stream2String(SendGETRequest(GenerateQueryString(parameters)));

            //removes all the \n\r
            res = Regex.Replace(res, @"\s*\r?\n\s*", "");

            if (!CheckError(res))
            {
                return (Convert.ToInt32(Regex.Match(res, "<tag id=\"(.*?)\">").Groups[1].Value));
            }

            return (-1);
        }

        /// <summary>
        /// Deletes a tag if the user is an admin or creator of the tag.
        /// </summary>
        /// <param name="tagID">The tag to delete.</param>
        /// <returns>true if success.</returns>
        public bool DeleteTag(long tagID)
        {
            ListDictionary parameters = new ListDictionary();
            parameters["method"] = "Tags.DeleteTag";
            parameters["tagID"] = Convert.ToString(tagID);

            FillParams(ref parameters);

            return (!CheckError(Stream2String(SendGETRequest(GenerateQueryString(parameters)))));
        }

        /// <summary>
        /// Retrieves a list of all the tags for a user.
        /// This includes tags the user has created as well as
        /// tags the user was invited to (and accepted).
        /// </summary>
        /// <returns>A Hashtable containing all the tags id mapped to their name.</returns>
        public Hashtable GetAllTags()
        {
            ListDictionary parameters = new ListDictionary();
            Hashtable tags = null;

            parameters["method"] = "Tags.GetAllTags";
            FillParams(ref parameters);

            string res = Stream2String(SendGETRequest(GenerateQueryString(parameters)));

            //removes all the \n\r
            res = Regex.Replace(res, @"\s*\r?\n\s*", "");

            if (!CheckError(res))
            {
                tags = new Hashtable();
                MatchCollection mc = Regex.Matches(res, "<tag id=\"(.*?)\">(.*?)</tag>");

                foreach (Match m in mc)
                {
                    tags.Add(m.Groups[2].Value, Convert.ToInt32(m.Groups[1].Value));
                }
            }

            return (tags);
        }

        #endregion


        #region Authentication methods

        /// <summary>
        /// Authorizes the specified user to log into openomy. This method is new and
        /// beta-testing by openomy developers. It removes the use of unconfirmedToken,
        /// returning directly the confirmed one.
        /// This method deprecates the use of OpenomyLogin method.
        /// </summary>
        /// <returns>true if success.</returns>
        public bool AuthorizeUser()
        {
            ListDictionary parameters = new ListDictionary();

            parameters["method"] = "Auth.AuthorizeUser";
            parameters["username"] = username;
            parameters["password"] = password;

            FillParams(ref parameters);

            string res = Stream2String(SendGETRequest(GenerateQueryString(parameters)));

            res = Regex.Replace(res, @"\s*\r?\n\s*", "");

            if (!CheckError(res))
            {
                confirmedToken = Regex.Match(res, "<confirmedtoken>(.*?)</confirmedtoken>").Groups[1].Value.ToLower();
                return (true);
            }

            return (false);
        }

        /// <summary>
        /// Takes a username, password, email address and registers the user (if the
        /// username/email haven't been registered, of course). If successful, it
        /// immediately returns a confirmedToken.
        /// </summary>
        /// <param name="username">The username to register.</param>
        /// <param name="password">The password to register.</param>
        /// <param name="email">The email of the new user.</param>
        /// <returns>true if success.</returns>
        public bool RegisterUser(string username, string password, string email)
        {
            ListDictionary parameters = new ListDictionary();

            parameters["method"] = "Auth.RegisterUser";
            parameters["username"] = username;
            parameters["password"] = password;
            parameters["email"] = email;

            FillParams(ref parameters);

            string res = Stream2String(SendGETRequest(GenerateQueryString(parameters)));

            res = Regex.Replace(res, @"\s*\r?\n\s*", "");

            if (!CheckError(res))
            {
                confirmedToken = Regex.Match(res, "<confirmedtoken>(.*?)</confirmedtoken>").Groups[1].Value.ToLower();
                return (true);
            }

            return (false);
        }


        #endregion


        #region HTTP methods

        /// <summary>
        /// Configures the proxy server for HTTP requests.
        /// </summary>
        /// <param name="uri">The url of proxy server + port.</param>
        /// <param name="username">Username to log in proxy.</param>
        /// <param name="password">Password to log in proxy.</param>
        private void setProxy(string uri, string username, string password)
        {
            //Credential definition
            NetworkCredential cred = new NetworkCredential(username, password);

            CredentialCache cache = new CredentialCache();
            cache.Add(new Uri(uri), "Basic", cred);

            WebProxy proxy = new WebProxy();

            Uri _uri = new Uri(uri);
            proxy.Address = _uri;
            proxy.Credentials = cache;
            //HttpWebRequest.DefaultWebProxy = proxy;
        }


        /// <summary>
        /// Configures the proxy server for HTTP requests, without credentials.
        /// Have to look out if it's useful...
        /// </summary>
        /// <param name="uri">The url of proxy server + port.</param>
        private void setProxy(string uri)
        {
            //WebRequest.DefaultWebProxy = new WebProxy(uri);
        }

        /// <summary>
        /// Sends an HTTP GET request to the server and receives the response.
        /// </summary>
        /// <param name="req">The query string of the request.</param>
        /// <returns>The stream to the server response.</returns>
        private Stream SendGETRequest(string req)
        {
            Console.WriteLine(openomyRootURL + req);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(openomyRootURL + req);
            request.Method = "GET";
            request.UserAgent = "TaskuBlogi 1.0";
            request.Timeout = 600000;

            Stream resStream = null;

            try
            {
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                resStream = response.GetResponseStream();
            }
            catch (WebException we)
            {
                Debug.WriteLine("Server Returned Error: " + we);
            }
            catch (TimeoutException) { Debug.WriteLine("Timeout."); }
            return (resStream);

        }

        #endregion


        #region Utility and debug methods

        private string Stream2String(Stream str)
        {

            StringBuilder sb = new StringBuilder();
            using (StreamReader sr = new StreamReader(str))
            {
                while (sr.Peek() != -1)
                {
                    sb.Append(sr.ReadToEnd());
                }
            }

            /*
            string tempString = null;
            int count = 0;
            byte[] buf = new byte[65535];

            do
            {
                //fill the buffer with data
                count = str.Read(buf, 0, buf.Length);

                if (count != 0)
                {
                    //translate from bytes to ASCII text
                    tempString = Encoding.ASCII.GetString(buf, 0, count);
                    sb.Append(tempString);
                }
            }
            while (count > 0);
            /**/

            str.Close();

            return (sb.ToString());
        }

        public bool CheckError(string response)
        {
            Debug.WriteLine(response, "OpenomyServerResponse");
            if (response.IndexOf("error") != -1) return true;
            else return false;
            //return (Regex.Match(response, "<error.*code=\"(.*)\">(.*)</error>").Success);
            // TODO: a method which parses error codes.
        }

        /// <summary>
        /// Adds the always needed parameters to the passed Dictionary.
        /// </summary>
        private void FillParams(ref ListDictionary parameters)
        {
            parameters["applicationKey"] = applicationKey;

            //needed by all methods except the authorization ones.
            if (!confirmedToken.Equals("")) parameters["confirmedToken"] = confirmedToken;

            parameters["signature"] = GenerateSignature(parameters);
        }


        /// <summary>
        /// Generates the query string based on the given Dictionary.
        /// </summary>
        /// <param name="parameters"></param>
        /// <returns>The query string with the form: ?attribute1=value1&attribute2=value2</returns>
        private string GenerateQueryString(ListDictionary parameters)
        {
            string querystring = "?";
            foreach (DictionaryEntry de in parameters)
            {
                querystring += de.Key + "=" + de.Value + "&";
            }
            //Console.WriteLine("\nquerystring: " + querystring);
            return querystring.Substring(0, querystring.Length - 1);

        }

        /// <summary>
        /// Calculates the MD5 hash of the query string.
        /// </summary>
        /// <param name="args">A dictionary containing the query string parameters.</param>
        /// <returns>An MD5 hash used for signature in Openomy API calls.</returns>
        private string GenerateSignature(ListDictionary parameters)
        {

            string sig = null;

            //sorts the parameters and put them all in a string
            ArrayList aKeys = new ArrayList(parameters.Keys);
            aKeys.Sort();

            foreach (string key in aKeys) sig += key + "=" + parameters[key];
            sig += privateKey;

            byte[] originalBytes;
            byte[] encodedBytes;
            MD5 md5;

            //Instantiate MD5CryptoServiceProvider, get bytes from original string and compute MD5 hash
            md5 = new MD5CryptoServiceProvider();
            originalBytes = Encoding.UTF8.GetBytes(sig);
            encodedBytes = md5.ComputeHash(originalBytes);

            //Convert encoded bytes back to a 'readable' string
            sig = BitConverter.ToString(encodedBytes).Replace("-", "");

            return (sig);
        }

        private string ResolveMimeType(string fileName)
        {
            switch (Path.GetExtension(fileName))
            {
                case ".txt":
                    return "text/plain";
                case ".html":
                    return "text/html";
                case ".htm":
                    return "text/html";
                case ".jpg":
                    return "image/jpeg";
                case ".png":
                    return "image/png";
                case ".gif":
                    return "image/gif";
                default:
                    return "application/octet-stream";
            }
        }

        #endregion
    }
}

public class OpenomyFile
{
    private string m_fileid;
    private string m_filename;
    private string m_creationdate;
    private string m_readaccess, m_writeaccess;
    private byte[] m_filedata;
    private long m_size;

    public OpenomyFile(string fileid, string filename)
    {
        m_fileid = fileid;
        m_filename = filename;
    }

    #region Property Sets/Gets

    public byte[] fileData
    {
        get { return m_filedata; }
        set { m_filedata = value; }
    }

    public string fileName
    {
        get { return m_filename; }
        set { m_filename = value; }
    }

    public string fileID
    {
        get { return m_fileid; }
        set { m_fileid = value; }
    }

    public string creationDate
    {
        get { return m_creationdate; }
        set { m_creationdate = value; }
    }

    public string readAccess
    {
        get { return m_readaccess; }
        set { m_readaccess = value; }
    }

    public string writeAccess
    {
        get { return m_writeaccess; }
        set { m_writeaccess = value; }
    }

    public long size
    {
        get { return m_size; }
        set { m_size = value; }
    }

    #endregion

}